Passed
Pull Request — master (#195)
by Alejandro
06:13
created

CreateShortUrl.js ➔ render   C

Complexity

Conditions 6

Size

Total Lines 117
Code Lines 107

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6.0585

Importance

Changes 0
Metric Value
eloc 107
dl 0
loc 117
ccs 15
cts 17
cp 0.8824
rs 6.0666
c 0
b 0
f 0
cc 6
crap 6.0585

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
import { faAngleDoubleDown as downIcon, faAngleDoubleUp as upIcon } from '@fortawesome/free-solid-svg-icons';
2
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3
import { assoc, dissoc, isEmpty, isNil, pipe, replace, trim } from 'ramda';
4
import React from 'react';
5
import { Collapse, FormGroup, Input } from 'reactstrap';
6
import * as PropTypes from 'prop-types';
7
import DateInput from '../utils/DateInput';
8
import Checkbox from '../utils/Checkbox';
9
import { serverType } from '../servers/prop-types';
10
import { compareVersions } from '../utils/utils';
11
import { createShortUrlResultType } from './reducers/shortUrlCreation';
12
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
13
14 1
const normalizeTag = pipe(trim, replace(/ /g, '-'));
15 2
const formatDate = (date) => isNil(date) ? date : date.format();
16
17 1
const CreateShortUrl = (
18
  TagsSelector,
19
  CreateShortUrlResult,
20
  ForServerVersion
21 1
) => class CreateShortUrl extends React.Component {
22 1
  static propTypes = {
23
    createShortUrl: PropTypes.func,
24
    shortUrlCreationResult: createShortUrlResultType,
25
    resetCreateShortUrl: PropTypes.func,
26
    selectedServer: serverType,
27
  };
28
29 1
  state = {
30
    longUrl: '',
31
    tags: [],
32
    customSlug: undefined,
33
    domain: undefined,
34
    validSince: undefined,
35
    validUntil: undefined,
36
    maxVisits: undefined,
37
    findIfExists: false,
38
    moreOptionsVisible: false,
39
  };
40
41
  render() {
42 8
    const { createShortUrl, shortUrlCreationResult, resetCreateShortUrl } = this.props;
43
44 8
    const changeTags = (tags) => this.setState({ tags: tags.map(normalizeTag) });
45 8
    const renderOptionalInput = (id, placeholder, type = 'text', props = {}) => (
46 24
      <FormGroup>
47
        <Input
48
          id={id}
49
          type={type}
50
          placeholder={placeholder}
51
          value={this.state[id]}
52 3
          onChange={(e) => this.setState({ [id]: e.target.value })}
53
          {...props}
54
        />
55
      </FormGroup>
56
    );
57 8
    const renderDateInput = (id, placeholder, props = {}) => (
58 16
      <div className="form-group">
59
        <DateInput
60
          selected={this.state[id]}
61
          placeholderText={placeholder}
62
          isClearable
63 2
          onChange={(date) => this.setState({ [id]: date })}
64
          {...props}
65
        />
66
      </div>
67
    );
68 8
    const save = (e) => {
69 1
      e.preventDefault();
70 1
      createShortUrl(pipe(
71
        dissoc('moreOptionsVisible'),
72
        assoc('validSince', formatDate(this.state.validSince)),
73
        assoc('validUntil', formatDate(this.state.validUntil))
74
      )(this.state));
75
    };
76 8
    const currentServerVersion = this.props.selectedServer ? this.props.selectedServer.version : '';
77 8
    const disableDomain = isEmpty(currentServerVersion) || compareVersions(currentServerVersion, '<', '1.19.0-beta.1');
78
79 8
    return (
80
      <div className="shlink-container">
81
        <form onSubmit={save}>
82
          <div className="form-group">
83
            <input
84
              className="form-control form-control-lg"
85
              type="url"
86
              placeholder="Insert the URL to be shortened"
87
              required
88
              value={this.state.longUrl}
89 1
              onChange={(e) => this.setState({ longUrl: e.target.value })}
90
            />
91
          </div>
92
93
          <Collapse isOpen={this.state.moreOptionsVisible}>
94
            <div className="form-group">
95
              <TagsSelector tags={this.state.tags} onChange={changeTags} />
96
            </div>
97
98
            <div className="row">
99
              <div className="col-sm-6">
100
                {renderOptionalInput('customSlug', 'Custom slug')}
101
              </div>
102
              <div className="col-sm-6">
103
                {renderOptionalInput('domain', 'Domain', 'text', {
104
                  disabled: disableDomain,
105
                  ...disableDomain && { title: 'Shlink 1.19.0 or higher is required to be able to provide the domain' },
106
                })}
107
              </div>
108
            </div>
109
110
            <div className="row">
111
              <div className="col-sm-6">
112
                {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
113
              </div>
114
              <div className="col-sm-3">
115
                {renderDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })}
116
              </div>
117
              <div className="col-sm-3">
118
                {renderDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })}
119
              </div>
120
            </div>
121
122
            <ForServerVersion minVersion="1.16.0">
123
              <div className="mb-4 text-right">
124
                <Checkbox
125
                  className="mr-2"
126
                  checked={this.state.findIfExists}
127
                  onChange={(findIfExists) => this.setState({ findIfExists })}
128
                >
129
                  Use existing URL if found
130
                </Checkbox>
131
                <UseExistingIfFoundInfoIcon />
132
              </div>
133
            </ForServerVersion>
134
          </Collapse>
135
136
          <div>
137
            <button
138
              type="button"
139
              className="btn btn-outline-secondary"
140
              onClick={() => this.setState(({ moreOptionsVisible }) => ({ moreOptionsVisible: !moreOptionsVisible }))}
141
            >
142
              <FontAwesomeIcon icon={this.state.moreOptionsVisible ? upIcon : downIcon} />
143
              &nbsp;
144
              {this.state.moreOptionsVisible ? 'Less' : 'More'} options
145
            </button>
146
            <button
147
              className="btn btn-outline-primary float-right"
148
              disabled={shortUrlCreationResult.loading || isEmpty(this.state.longUrl)}
149
            >
150
              {shortUrlCreationResult.loading ? 'Creating...' : 'Create'}
151
            </button>
152
          </div>
153
154
          <CreateShortUrlResult {...shortUrlCreationResult} resetCreateShortUrl={resetCreateShortUrl} />
155
        </form>
156
      </div>
157
    );
158
  }
159
};
160
161
export default CreateShortUrl;
162